﻿using Abp.Runtime.Security;
using Hicap.QRCodeWarehouse.Core.Common;
using Microsoft.AspNetCore.SignalR;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;

namespace Hicap.QRCodeWarehouse.Core.Notify
{
    public class NotifyHub : Hub
    {
        private static readonly object LockObject = new object();

        private static Dictionary<string, List<string>> ConnectedClient { get; set; } =
            new Dictionary<string, List<string>>()
            {
                {SpeicaAuthCode.WarhouseMonitor, new List<string>()},
                {SpeicaAuthCode.FileRepository, new List<string>()},
                {SpeicaAuthCode.GenerationTask, new List<string>()},
            };

        private string UserId
        {
            get
            {
                if (Context?.User == null)
                {
                    return "anonymous";
                }

                IEnumerable<Claim> claims = Context.User.Claims;

                Claim userIdClaim = claims.FirstOrDefault(x => x.Type.Equals(AbpClaimTypes.UserId));

                return userIdClaim != null ? userIdClaim.Value : "anonymous";
            }
        }

        private bool HaveWarhouseMonitorAuth(string authCode)
        {
            if (Context?.User == null)
            {
                return false;
            }

            IEnumerable<Claim> claims = Context.User.Claims;

            Claim authsClaim = claims.FirstOrDefault(x => x.Type.Equals(ClaimTypes.AuthorizationDecision));

            if (authsClaim != null)
            {
                return authsClaim.Value.Split(",").Any(x => x.Equals(authCode));
            }

            return false;
        }

        public override async Task OnConnectedAsync()
        {
            await Clients.Client(Context.ConnectionId)
                .SendAsync(NotificationMethod.Connected, $"{Context.ConnectionId} is Connected.");

            bool flag = false;
            foreach (string key in ConnectedClient.Keys)
            {
                if (HaveWarhouseMonitorAuth(key))
                {
                    flag = true;
                    ConnectedClient[key].Add(Context.ConnectionId);
                    Groups.AddToGroupAsync(Context.ConnectionId, key).GetAwaiter().GetResult();
                }
            }

            if (flag) await Clients.Client(Context.ConnectionId).SendAsync(NotificationMethod.Connected, "{Auth:true}");
            else await Clients.Client(Context.ConnectionId).SendAsync(NotificationMethod.Connected, "{Auth:false}");

            await base.OnConnectedAsync();
        }

        public override async Task OnDisconnectedAsync(Exception exception)
        {
            lock (LockObject)
            {
                List<string> keyList = new List<string>();
                foreach (string key in ConnectedClient.Keys)
                {
                    if (ConnectedClient[key].Any(x => x.Equals(Context.ConnectionId)))
                    {
                        keyList.Add(key);
                    }
                }

                keyList.ForEach(x =>
                {
                    ConnectedClient[x].Remove(Context.ConnectionId);
                    try
                    {
                        Groups.RemoveFromGroupAsync(Context.ConnectionId, x).GetAwaiter().GetResult();
                    }
                    catch
                    {
                        // ignored
                    }
                });
            }

            await base.OnDisconnectedAsync(exception);
        }

        public async Task SendToGroup(string method, string message)
        {
            if (string.IsNullOrEmpty(message)) return;
            if (string.IsNullOrEmpty(UserId)) return;
            if (Clients?.Groups(UserId) == null) return;

            foreach (KeyValuePair<string, List<string>> keyValuePair in NotificationMethod.AllowNotificationConfig)
            {
                if (keyValuePair.Value.Any(x => x.Equals(method)))
                {
                    if (ConnectedClient[keyValuePair.Key].Any())
                    {
                        await Clients.Group(keyValuePair.Key).SendAsync(method, message);

                        Log.Information($"Send to signalr group[{keyValuePair.Key}] clients[{string.Join(",", ConnectedClient[keyValuePair.Key])}] :[{method}] {message}");
                        break;
                    }
                }
            }
        }
    }
}